#include "mserLocate.h"
#include "colorLocate.h"
#include "debug.h"
#include <iostream>

using namespace cv;
using namespace std;

//enum Color { BLUE, YELLOW, WHITE, UNKNOWN };

static Mat colorMatch(const Mat &src, Mat &match, const Color r, const bool adaptive_minsv) {

	// if use adaptive_minsv
	// min value of s and v is adaptive to h

	const float max_sv = 255;
	const float minref_sv = 64;

	const float minabs_sv = 95;

	// H range of blue 

	const int min_blue = 100;  // 100
	const int max_blue = 140;  // 140

	// H range of yellow

	const int min_yellow = 15;  // 15
	const int max_yellow = 40;  // 40

	// H range of white

	const int min_white = 0;   // 15
	const int max_white = 30;  // 40

	Mat src_hsv;

	// convert to HSV space
	cvtColor(src, src_hsv, CV_BGR2HSV);

	std::vector<cv::Mat> hsvSplit;
	split(src_hsv, hsvSplit);
	equalizeHist(hsvSplit[2], hsvSplit[2]);
	merge(hsvSplit, src_hsv);

	// match to find the color

	int min_h = 0;
	int max_h = 0;
	switch (r) {
		case BLUE:
			min_h = min_blue;
			max_h = max_blue;
			break;
		case YELLOW:
			min_h = min_yellow;
			max_h = max_yellow;
			break;
		case WHITE:
			min_h = min_white;
			max_h = max_white;
			break;
		default:
			// Color::UNKNOWN
			break;
	}

	float diff_h = float((max_h - min_h) / 2);
	float avg_h = min_h + diff_h;

	int channels = src_hsv.channels();
	int nRows = src_hsv.rows;

	// consider multi channel image
	int nCols = src_hsv.cols * channels;
	if (src_hsv.isContinuous()) {
		nCols *= nRows;//总共有多少单像素
		nRows = 1;
	}

	int i, j;
	uchar* p;
	float s_all = 0;
	float v_all = 0;
	float count = 0;
	for (i = 0; i < nRows; ++i) {
		p = src_hsv.ptr<uchar>(i);
		for (j = 0; j < nCols; j += 3) {
			int H = int(p[j]);      // 0-180
			int S = int(p[j + 1]);  // 0-255
			int V = int(p[j + 2]);  // 0-255

			s_all += S;
			v_all += V;
			count++;

			bool colorMatched = false;

			if (H > min_h && H < max_h) {
				float Hdiff = 0;
				if (H > avg_h)
					Hdiff = H - avg_h;
				else
					Hdiff = avg_h - H;

				float Hdiff_p = float(Hdiff) / diff_h;

				float min_sv = 0;
				if (true == adaptive_minsv)
					min_sv =
						minref_sv -
						minref_sv / 2 *
						(1
						 - Hdiff_p);  // inref_sv - minref_sv / 2 * (1 - Hdiff_p)
				else
					min_sv = minabs_sv;  // add

				if ((S > min_sv && S < max_sv) && (V > min_sv && V < max_sv))
					colorMatched = true;
			}

			if (colorMatched == true) {
				p[j] = 0;
				p[j + 1] = 0;
				p[j + 2] = 255;
			} else {
				p[j] = 0;
				p[j + 1] = 0;
				p[j + 2] = 0;
			}
		}
	}

	// cout << "avg_s:" << s_all / count << endl;
	// cout << "avg_v:" << v_all / count << endl;

	// get the final binary

	Mat src_grey;
	std::vector<cv::Mat> hsvSplit_done;
	split(src_hsv, hsvSplit_done);
	src_grey = hsvSplit_done[2];

	match = src_grey;

	return src_grey;
}

bool verifySizes(RotatedRect mr, Mat src) {	
	int DEFAULT_VERIFY_MIN = 1;   // 3
	int DEFAULT_VERIFY_MAX = 240;  // 20
	
	float error = 0.9;
	// Spain car plate size: 52x11 aspect 4,7272
	// China car plate size: 440mm*140mm，aspect 3.142857

	// Real car plate size: 136 * 32, aspect 4
	float aspect = 3.75;

	// Set a min and max area. All other patchs are discarded
	// int min= 1*aspect*1; // minimum area
	// int max= 2000*aspect*2000; // maximum area
	int min = 34 * 8 * DEFAULT_VERIFY_MIN;  // minimum area
	int max = 34 * 8 * DEFAULT_VERIFY_MAX;  // maximum area

	// Get only patchs that match to a respect ratio.
	float rmin = aspect - aspect * error;
	float rmax = aspect + aspect * error;

	float area = mr.size.height * mr.size.width;
	float r = (float) mr.size.width / (float) mr.size.height;
	if (r < 1) r = (float) mr.size.height / (float) mr.size.width;

	//cout << "area:" << area << endl;
	//cout << "r:" << r << endl;
	//cout << "min " << min << " max " << max << " rmin " << rmin << "rmax" << rmax <<endl;
	//showRotatedRect(mr, src);

	if ( (area < min || area > max) || (r < rmin || r > rmax)) //(area < min || area > max) ||
		return false;
	else
		return true;
}

static int colorSearch(const Mat &src, const Color r, Mat &out,
		vector<RotatedRect> &outRects) {
	Mat match_grey;

	// width is important to the final results;

	const int color_morph_width = 10;
	const int color_morph_height = 2;

	colorMatch(src, match_grey, r, false);

	Mat src_threshold;
	threshold(match_grey, src_threshold, 0, 255,
			CV_THRESH_OTSU + CV_THRESH_BINARY);

	Mat element = getStructuringElement(
			MORPH_RECT, Size(color_morph_width, color_morph_height));
	morphologyEx(src_threshold, src_threshold, MORPH_CLOSE, element);

	src_threshold.copyTo(out);

	vector< vector<Point> > contours;

	findContours(src_threshold,
			contours,				// a vector of contours
			CV_RETR_EXTERNAL,
			CV_CHAIN_APPROX_NONE);	// all pixels of each contours

	vector< vector<Point> >::iterator itc = contours.begin();
	while (itc != contours.end()) {
		RotatedRect mr = minAreaRect(Mat(*itc));

		if (!verifySizes(mr, src))
			itc = contours.erase(itc);
		else {
			++itc;
			outRects.push_back(mr);
		}

		
		//showRotatedRect(mr, src);
	}

	return 0;
}

//nt deskew(const Mat &src, const Mat &src_b, vector<RotatedRect> &inRects, vector<Mat> &outPlatesMat)

int colorLocate(Mat src, vector<Mat> &candPlates) {
	vector<RotatedRect> rects_color_blue;
	rects_color_blue.reserve(64);
	vector<RotatedRect> rects_color_yellow;
	rects_color_yellow.reserve(64);

	vector<Mat> plates_blue;
	plates_blue.reserve(64);
	vector<Mat> plates_yellow;
	plates_yellow.reserve(64);

	Mat src_clone = src.clone();

	Mat src_b_blue;
	Mat src_b_yellow;

	{
		colorSearch(src, BLUE, src_b_blue, rects_color_blue);
		deskew(src, src_b_blue, rects_color_blue, plates_blue);
		//showAndDestroy(plates_blue);
	}

	{
		colorSearch(src_clone, YELLOW, src_b_yellow, rects_color_yellow);
		deskew(src_clone, src_b_yellow, rects_color_yellow, plates_yellow);
		//showAndDestroy(plates_yellow);
	}

	candPlates.insert(candPlates.end(), plates_blue.begin(), plates_blue.end());
	candPlates.insert(candPlates.end(), plates_yellow.begin(), plates_yellow.end());

	return 0;
}

